package busuanzi.util;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Maps;
import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
import com.googlecode.concurrentlinkedhashmap.Weighers;

import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * Auth: bruce-sha
 * Date: 2015/5/6
 * cache中有值就立即获取并返回，将callback异步写入新的值
 * AsynCache
 */
public abstract class ACache {

    static class Executor {
        static final int cores = Runtime.getRuntime().availableProcessors();
        static final ExecutorService executor = Executors.newFixedThreadPool(cores + 1);
    }

    static class MapCache {
        static final ConcurrentMap cache = Maps.newConcurrentMap();
    }

    public static <K, V> V mget(K k, Loader<K, V> loader) throws Exception {
        if (MapCache.cache.containsKey(k)) {
            // 异步执行
            Executor.executor.submit(() -> {
                V v = loader.load();
                MapCache.cache.put(k, v);
                return v;
            });
            return (V) MapCache.cache.get(k);
        } else {
            // 同步执行
            V v = loader.load();
            MapCache.cache.put(k, v);
            return v;
        }
    }

    static class GuavaCache {
        static final Cache cache = CacheBuilder.newBuilder()
                // .weakKeys() //有bug，相同url每次都认为不同key
                .softValues()
                .maximumSize(10000)
                .expireAfterAccess(12, TimeUnit.HOURS) // .recordStats()
                .build();
    }

    public static <K, V> V get(K k, Loader<K, V> loader) throws Exception {
        V c = (V) GuavaCache.cache.getIfPresent(k);
        if (c != null) {
            // 异步执行
            Executor.executor.submit(() -> {
                V v = loader.load();
                GuavaCache.cache.put(k, v);
                return v;
            });
            return c;
        } else {
            // 同步执行
            return (V) GuavaCache.cache.get(k, () -> loader.load());
        }
    }

    static class LRUCache {
        ConcurrentLinkedHashMap<Object, Object> cache = new ConcurrentLinkedHashMap
                .Builder<Object, Object>()
                .maximumWeightedCapacity(1000)
                .weigher(Weighers.singleton())
                .build();
    }

    static class RedisCache {

    }

    public interface Loader<K, V> {
        V load() throws Exception;
    }

}
